package com.hengx.cellview.app;

import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.view.animation.LinearInterpolator;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class WaterBackground extends Drawable {

    public static final int DEFAULT_RIPPLE_DURATION = 1500; // 涟漪动画时长
    public static final float DEFAULT_MAX_RIPPLE_RADIUS = 0.5f;
    public static final float DEFAULT_MIN_RIPPLE_RADIUS = 0.05f;
    public static final float DEFAULT_RIPPLE_WIDTH = 0.01f;

    private int backgroundColor = 0x20000000;
    private int rippleColor = 0x30ffffff;
    private final List<Ripple> ripples = new ArrayList<>();
    private Handler handler;
    private boolean isAnimating;
    private long animPeriod = 16;

    public WaterBackground(){
        init();
    }
    public WaterBackground(boolean isDark){
        this.backgroundColor = isDark ? 0x20ffffff : 0x20000000;
        this.rippleColor = isDark ? 0x30ffffff : 0x30000000;
        init();
    }
    public WaterBackground(int backgroundColor, int rippleColor){
        this.backgroundColor = backgroundColor;
        this.rippleColor = rippleColor;
        init();
    }

    private void init(){
        this.paint = new Paint();
        paint.setAntiAlias(true);
        handler = new Handler(Looper.getMainLooper());
        for (int i = 0; i < 5; i++) {
            addRipple(new Ripple(rippleColor));
        }
    }

    private Paint paint;
    private ValueAnimator animator;
    private int alpha;
    private float radius, maxRadius;
    private boolean isShow;
    private float x, y;

    @Override
    public void draw(@NonNull Canvas canvas) {
        int bg = Color.argb(alpha, Color.red(backgroundColor), Color.green(backgroundColor), Color.blue(backgroundColor));
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(bg);
        Rect rect = getBounds();
        if (x == 0) x = rect.centerX();
        if (y == 0) y = rect.centerY();
        canvas.drawCircle(x, y, radius, paint);
        if(isAnimating){
            for (Ripple ripple : ripples) {
                ripple.draw(rect.left, rect.top, canvas);
            }
        }

    }

    @Override
    public void setAlpha(int i) {

    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {

    }

    public void addRipple(Ripple ripple) {
        if (ripple == null)
            return;
        ripple.measure(getBounds().width(), getBounds().height());
        ripples.add(ripple);
    }

    public void removeRipple(int index) {
        ripples.remove(index);
    }

    public void clearRipples() {
        ripples.clear();
    }

    public int getRippleCount() {
        return ripples.size();
    }

    public Ripple getRipple(int index) {
        return ripples.get(index);
    }

    public void setAnimPeriod(long animPeriod) {
        this.animPeriod = animPeriod;
    }

    public long getAnimPeriod() {
        return this.animPeriod;
    }

    /**
     * 启动动画
     */
    public void startRippleAnimation() {
        if (!isAnimating) {
            isAnimating = true;
                handler.postDelayed(rippleRunnable, 16); // 60fps
        }
    }

    /**
     * 立即停止动画
     */
    public void stopRippleAnimation() {
        isAnimating = false;
        handler.removeCallbacks(rippleRunnable);
    }

    private final Runnable rippleRunnable = new Runnable() {
        @Override
        public void run() {
            updateRipples();
            invalidateSelf();
            handler.postDelayed(this, 16); // 60fps
        }
    };

    private void updateRipples() {
        for (Ripple ripple : ripples) {
            ripple.measure(getBounds().width(), getBounds().height());
            ripple.update();
            if (ripple.isReadyToRestart()) {
                ripple.reset();
            }
        }
    }

    @Override
    protected boolean onStateChange(@NonNull int[] state) {
        boolean enable = false;
        boolean pressed = false;
        for(int s : state){
            switch (s){
                case android.R.attr.state_pressed:
                    pressed = true;
                    break;
                case android.R.attr.state_enabled:
                    enable = true;
                    break;
            }
        }
        if(!enable) return false;
        if(pressed){
            show();
            return true;
        }else if (isShow){
            hide();
            return true;
        }else {
            return false;
        }
    }

    @Override
    public boolean isStateful() {
        return true;
    }

    @Override
    public void setHotspot(float x, float y) {
        if (isAnimating) {
            this.x = x;
            this.y = y;
        }
    }

    public void show(){
        startRippleAnimation();
        if(animator != null && animator.isRunning()) animator.cancel();
        isShow = true;
        alpha = 0;
        radius = 0;
        animator = ValueAnimator.ofFloat(0, getRadius());
        animator.setDuration(300);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(valueAnimator -> {
            radius = (float) valueAnimator.getAnimatedValue();
            alpha = (int) (valueAnimator.getAnimatedFraction() * Color.alpha(backgroundColor));
            invalidateSelf();
        });
        animator.start();
    }

    public void hide(){
        stopRippleAnimation();
        //if(animator != null && animator.isRunning()) animator.cancel();
        isShow = false;
        alpha = Color.alpha(backgroundColor);
        animator = ValueAnimator.ofInt(alpha, 0);
        animator.setDuration(300);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(valueAnimator -> {
            alpha = (int) valueAnimator.getAnimatedValue();
            invalidateSelf();
        });
        animator.start();
    }

    public float getRadius(){
        int w = getBounds().width();
        int h = getBounds().height();
        double d = Math.sqrt(w * w + h * h);
        return (float) (d);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSPARENT;
    }

    public int getRippleColor() {
        return rippleColor;
    }

    public void setRippleColor(int rippleColor) {
        this.rippleColor = rippleColor;
    }

    public static class Ripple {

        private float maxRadius = DEFAULT_MAX_RIPPLE_RADIUS;
        private float minRadius = DEFAULT_MIN_RIPPLE_RADIUS;
        private float toRadius;
        private float cx;
        private float cy;
        private float radius;
        private long startTime;
        private long waitTime;
        private Random random;
        private int viewWidth, viewHeight;
        private Paint paint;
        private float rippleWidth = DEFAULT_RIPPLE_WIDTH;
        private int color;
        private long duration = DEFAULT_RIPPLE_DURATION;

        public Ripple() {
            this(0x80808080);
        }

        public Ripple(int color) {
            this.color = color;
            init();
        }

        private void init() {
            this.paint = new Paint();
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.STROKE);
            random = new Random();
            reset();
        }

        public void reset() {
            cx = random.nextFloat();
            cy = random.nextFloat();
            radius = 0;
            int mx = (int) (maxRadius * 100f);
            int mn = (int) (minRadius * 100f);
            int l = (int) (Math.random() * (mx + 1 - mn) + mn);
            toRadius = (l / 100f);
            waitTime = (long) (Math.random() * duration);
            startTime = System.currentTimeMillis() + waitTime;
        }

        public void update() {
            if (System.currentTimeMillis() >= startTime) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                float progress = (float) elapsedTime / duration;
                float to = (Math.max(viewWidth, viewHeight) * toRadius);
                radius = (to * progress) / 2f;
                int alpha = (int) (Color.alpha(color) * (1 - progress));
                paint.setColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)));
                paint.setStrokeWidth(Math.min(viewWidth, viewHeight) * rippleWidth);
                if (progress >= 1.0f) {
                    reset();
                }
            }
        }

        public void draw(float baseX, float baseY, Canvas canvas) {
            if (System.currentTimeMillis() >= startTime) {
                canvas.drawCircle(baseX + (viewWidth * cx), baseY + (viewHeight * cy), radius, paint);
            }
        }

        public boolean isReadyToRestart() {
            return System.currentTimeMillis() >= startTime + waitTime + duration;
        }

        public void setPaint(Paint paint) {
            this.paint = paint;
        }

        public Paint getPaint() {
            return this.paint;
        }

        public void setDuration(long duration) {
            this.duration = duration;
        }

        public long getDuration() {
            return this.duration;
        }

        public void setMaxRadius(float maxRadius) {
            this.maxRadius = maxRadius;
        }

        public float getMaxRadius() {
            return this.maxRadius;
        }

        public float getMinRadius() {
            return minRadius;
        }

        public void setMinRadius(float minRadius) {
            this.minRadius = minRadius;
        }

        public int getColor() {
            return color;
        }

        public void setColor(int color) {
            this.color = color;
        }

        public void measure(int width, int height) {
            this.viewWidth = width;
            this.viewHeight = height;
        }

        public float getRippleWidth() {
            return rippleWidth;
        }

        public void setRippleWidth(float rippleWidth) {
            this.rippleWidth = rippleWidth;
        }
    }

}
